﻿using Extended.WPF.Core;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows;
using System.Windows.Data;

namespace Extented.UI.Core.Helpers
{
    public class GlobalThemeHelper : DependencyObject
    {
        public static readonly DependencyProperty IsGlobalThemeNameProperty;
        static GlobalThemeHelper instance = new GlobalThemeHelper();
        string applicationThemeName;
        static GlobalThemeHelper()
        {
            IsGlobalThemeNameProperty = DependencyProperty.RegisterAttached("IsGlobalThemeName", typeof(bool), typeof(GlobalThemeHelper), new FrameworkPropertyMetadata(false));
        }
        protected GlobalThemeHelper() { }
        public string ApplicationThemeName
        {
            get { return applicationThemeName; }
            set { SetApplicationThemeName(value); }
        }
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static bool GetIsGlobalThemeName(DependencyObject d)
        {
            return (bool)d.GetValue(IsGlobalThemeNameProperty);
        }
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static void SetIsGlobalThemeName(DependencyObject d, bool value)
        {
            d.SetValue(IsGlobalThemeNameProperty, value);
        }
        public static GlobalThemeHelper Instance
        {
            get
            {
                if (instance == null)
                    instance = new GlobalThemeHelper();
                return instance;
            }
        }
        IThemeManager ThemeManager { get { return Extended.WPF.Core.ThemeManager.Instance; } }
        internal static bool IsAppInitialized
        {
            get { return Application.Current != null && (Application.Current.StartupUri != null || Application.Current.MainWindow != null); }
        }
        bool AppliedUserTheme(FrameworkElement window)
        {
            return !string.IsNullOrEmpty(ThemeManager.GetThemeName(window)) && !GetIsGlobalThemeName(window) || HasBinding(window);
        }
        bool HasBinding(FrameworkElement window)
        {
            BindingExpression expression = ThemeManager.GetThemeNameBindingExpression(window);
            if (expression == null)
                return false;
            return expression.Status != BindingStatus.Detached;
        }
        void SetApplicationThemeName(string value)
        {
            if (applicationThemeName == value)
                return;
            applicationThemeName = value;
            InitializeWindowTracker();
            SetWindowsApplicationThemeNameInThread();
        }
        [MethodImpl(MethodImplOptions.NoInlining)]
        static void InitializeWindowTracker()
        {
            WindowTracker.Initialize();
        }
        internal void SetWindowsApplicationThemeNameInThread()
        {
            if (Application.Current == null)
                return;
            if (!IsAppInitialized)
            {
                Application.Current.Dispatcher.BeginInvoke((Action)SetWindowsApplicationThemeName);
            }
            else
            {
                Application.Current.Dispatcher.Invoke((Action)SetWindowsApplicationThemeName);
            }
        }
        void SetWindowsApplicationThemeName()
        {
            lock (locker)
            {
                SetApplicationWindows();
                SetNonApplicationWindows();
            }
        }
        object locker = new object();
        void SetApplicationWindows()
        {
            if (Application.Current == null)
                return;
            foreach (Window window in Application.Current.Windows)
            {
                AssignApplicationThemeName(window);
            }
        }
        void SetNonApplicationWindows()
        {
            if (SecurityHelper.IsPartialTrust || Application.Current == null)
                return;
            foreach (Window window in (WindowCollection)Application.Current.GetValue("NonAppWindowsInternal", BindingFlags.NonPublic | BindingFlags.Instance))
            {
                System.Threading.ThreadState windowThreadState = window.Dispatcher.Thread.ThreadState;
                if (!CheckThreadExecutable(windowThreadState))
                    continue;
                if (CheckThreadInDirectAccess(windowThreadState))
                    window.Dispatcher.Invoke((Action<Window>)AssignApplicationThemeName, window);
                else
                    window.Dispatcher.BeginInvoke((Action<Window>)AssignApplicationThemeName, window);
            }
        }
        bool CheckThreadInDirectAccess(System.Threading.ThreadState state)
        {
            return (state & System.Threading.ThreadState.WaitSleepJoin) == 0;
        }
        bool CheckThreadExecutable(System.Threading.ThreadState state)
        {
            if ((state & System.Threading.ThreadState.Running) != 0 ||
                (state & System.Threading.ThreadState.Background) != 0)
                return true;
            return false;
        }
        public void AssignApplicationThemeName(FrameworkElement window)
        {
            if (!AppliedUserTheme(window))
            {
                SetGlobalTheme(window);
            }
        }
        void SetGlobalTheme(FrameworkElement window)
        {
            if (string.IsNullOrEmpty(ApplicationThemeName))
            {
                ThemeManager.ClearThemeName(window);
                SetIsGlobalThemeName(window, false);
            }
            else
            {
                ThemeManager.SetThemeName(window, ApplicationThemeName);
                SetIsGlobalThemeName(window, true);
            }
        }
    }
}
